package com.tianheng.wanzhou.datacollecting.netty.jiangsha;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * @Author: yujw
 * @Date: 2020/5/12 15:56
 * @Version 1.0
 */
@Slf4j
@Component
public class GatherServer implements Runnable{
    private String name="GatherServer";

    @Resource
    GatherServerHandler gatherServerHandler;

    private int port;

    public void setPort(int port) {
        this.port = port;

    }

    @Override
    public void run() {
        //Group：群组，Loop：循环，Event：事件，这几个东西联在一起，相比大家也大概明白它的用途了。
        //Netty内部都是通过线程在处理各种数据，EventLoopGroup就是用来管理调度他们的，注册Channel，管理他们的生命周期。
        //NioEventLoopGroup是一个处理I/O操作的多线程事件循环
        //bossGroup作为boss,接收传入连接
        //因为bossGroup仅接收客户端连接，不做复杂的逻辑处理，为了尽可能减少资源的占用，取值越小越好

        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //workerGroup作为worker，处理boss接收的连接的流量和将接收的连接注册进入这个worker

        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            log.info(name + "启动GatherServer的服务器（浆纱），端口"+port);
            //ServerBootstrap负责建立服务端
            //你可以直接使用Channel去建立服务端，但是大多数情况下你无需做这种乏味的事情
            ServerBootstrap server = new ServerBootstrap();
            // 1. 绑定两个线程组分别用来处理客户端通道的accept和读写时间
            server.group(bossGroup, workerGroup)
                    // 2. 绑定服务端通道NioServerSocketChannel
                    .channel(NioServerSocketChannel.class)
                    // 3. 给读写事件的线程通道绑定handler去真正处理读写
                    // ChannelInitializer初始化通道SocketChannel
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel){
                            // 请求解码器
                            //指定分隔符
                            //ByteBuf delimiter = Unpooled.copiedBuffer("3DD3".getBytes("UTF-8"));
                            //socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(10000,delimiter));
                            //socketChannel.pipeline().addLast("FixedLengthFrameDecoder", new FixedLengthFrameDecoder(10));
                            //socketChannel.pipeline().addLast("LengthFieldBasedFrameDecoder",new LengthFieldBasedFrameDecoder(1024,4,2,0,0));
                            //socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                            // 将2进制字节流转成字符串
                            //socketChannel.pipeline().addLast("StringDecoder", new StringDecoder());
//                            socketChannel.pipeline().addLast("GatherDecoder", new GatherDecoder());
                            // 自定义处理handler
                            socketChannel.pipeline().addLast("gather-server-handler", gatherServerHandler);
                        }
                    })//对Channel进行一些配置
                    //注意以下是socket的标准参数
                    //BACKLOG用于构造服务端套接字ServerSocket对象，标识当服务器请求处理线程全满时，用于临时存放已完成三次握手的请求的队列的最大长度。如果未设置或所设置的值小于1，Java将使用默认值50。
                    //Option是为了NioServerSocketChannel设置的，用来接收传入连接的
                    .option(ChannelOption.SO_BACKLOG, 128)
                    //是否启用心跳保活机制。在双方TCP套接字建立连接后（即都进入ESTABLISHED状态）并且在两个小时左右上层没有任何数据传输的情况下，这套机制才会被激活。
                    //childOption是用来给父级ServerChannel之下的Channels设置参数的
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            try {
                // 4. 监听端口（服务器host和port端口），同步返回
                ChannelFuture future = server.bind(this.port).sync();
                // 当通道关闭时继续向后执行，这是一个阻塞方法
                //sync()会同步等待连接操作结果，用户线程将在此wait()，直到连接操作完成之后，线程被notify(),用户代码继续执行
                //closeFuture()当Channel关闭时返回一个ChannelFuture,用于链路检测
                future.channel().closeFuture().sync();
            }catch (Exception e){
                e.printStackTrace();
            }

        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
